#朴素贝叶斯分类器 Naive Bayes Classifier

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm                               #colormap 颜色地图
from sklearn.datasets import make_blobs                 #是为聚类产生数据集产生一个数据集和相应的标签
from sklearn.naive_bayes import GaussianNB              #先验为高斯分布的朴素贝叶斯
from sklearn.metrics import brier_score_loss            #衡量概率校准的一个参数,Brier分数对于一组预测值越低，预测校准越好
from sklearn.calibration import CalibratedClassifierCV  #交叉验证生成器
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

plt.rcParams['font.sans-serif']=['SimHei']              #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False                #用来正常显示负号

n_samples = 50000
centers = [(-5, -5), (0, 0), (5, 5)]

X,y=make_blobs(n_samples=n_samples,n_features=2,cluster_std=2.5,centers=centers,shuffle=False, random_state=42)
    #总样本数 50000；样本点的维度 2；样本中，簇的标准差 2.5；样本中心数；不打乱。返回X样本，y标签

y[:n_samples // 2] = 0    # 前25000个样本标签置为0
y[n_samples // 2:] = 1    # 后25000个样本标签置为1
sample_weight = np.random.RandomState(42).rand(y.shape[0])   #50000个随机值  ？？这个数组是干嘛的？
#解决了，这个sample_weight 是样本权重，在下面的brier_score_loss 评价中会用到
X_train, X_test, y_train, y_test,sw_train,sw_test = train_test_split(X, y, sample_weight, test_size=0.9, random_state=42)

'''
https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html#sklearn.model_selection.train_test_split
这里传入X,y,sample_weight，接受变量是3组，6个，都分割为train和test；
'''
# X_train, X_test, y_train, y_test,sw_train,sw_test = train_test_split(X, y, test_size=0.9, random_state=42)
# 6个接受，4个返回

clf = GaussianNB()
clf.fit(X_train, y_train)
# a = clf.predict_proba(X_test)  #45000*2
prob_pos_clf = clf.predict_proba(X_test)[:, 1]        #测试向量的概率预估  45000*1
target_pred = clf.predict(X_test)

score = accuracy_score(y_test, target_pred, normalize = True)
print("accuracy score:",score)

clf_score = brier_score_loss(y_test, prob_pos_clf, sw_test)
print("clf_score: %1.3f" % clf_score)

plt.figure()                                          #绘图
y_unique = np.unique(y)                               #去除数组中的重复数字，并进行排序之后输出 y只有0，1
patterns = ['^','o']                                  #cm.rainbow(np.linspace(0.0, 1.0, y_unique.size)) 点的样式

for this_y, marker in zip(y_unique, patterns):
    this_X = X_train[y_train == this_y]
    this_sw = sw_train[y_train == this_y]
    plt.scatter(this_X[:, 0], this_X[:, 1], s=this_sw * 50, marker=marker,
                alpha=0.5, edgecolor='k',
                label="类别 %s" % this_y)

plt.legend(loc="best")
plt.xlabel("x坐标")
plt.ylabel("y坐标")
plt.title("样本数据")
plt.show()

plt.figure()
order = np.lexsort((prob_pos_clf, ))
plt.plot(prob_pos_clf[order], 'r')
plt.ylim([-0.05, 1.05])
plt.xlabel("样本")
plt.ylabel("P(y=1)")
plt.title("高斯贝叶斯分类")
plt.show()


'''
在scikit-learn中，一共有3个朴素贝叶斯的分类算法类。
分别是GaussianNB，MultinomialNB和BernoulliNB。
其中GaussianNB就是先验为高斯分布的朴素贝叶斯，
MultinomialNB就是先验为多项式分布的朴素贝叶斯，
而BernoulliNB就是先验为伯努利分布的朴素贝叶斯。

Brier分数可以被认为是对一组概率预测的“校准”的量度，或者称为“ 成本函数 ”，
这一组概率对应的情况必须互斥，并且概率之和必须为1
Brier分数对于一组预测值越低，预测校准越好

概率校准就是对分类函数做出的分类预测概率重新进行计算，并且计算Brier分数，
然后依据Brier分数的大小判断对初始预测结果是支持还是反对。

'''